<?php

namespace Shared\LocalSyntaxSpecific\HTML;

/**
 * 
 * @author Jordan Minchev <j.minchev@object.bg>
 * @copyright (c) 2014, ASPECT.BG Ltd.
 */
class Parser {

	/**
	 * An array of last element for each level
	 * @var array
	 */
	public $elements = array();
	public $allElements = array();

	/**
	 * Current element number
	 * @var int 
	 */
	private $counter = 0;

	/**
	 * Current Depth reached
	 * @var int
	 */
	private $level = 0;

	/**
	 * The content
	 * @var string
	 */
	private $content = '';

	/**
	 * Search array for replacement
	 * @var array 
	 */
	private $ids = array();

	/**
	 * Replace array containing the real html tags and their attributes
	 * @var array 
	 */
	private $tags = array();

	/**
	 * @var string 
	 */
	private $tagIDprefix = '{$_';

	/**
	 * @var string 
	 */
	private $tagIDsuffix = '_$} ';

	/**
	 * @var string
	 */
	private $nbspReplace;

	public function __construct() {
		$this->nbspReplace = $this->tagIDprefix . 'nbsp' . $this->tagIDsuffix . ' ';
	}

	/**
	 * 
	 * @param string $text
	 * @return string
	 */
	public function load($text) {
		$this->tags = $this->ids = $this->elements = array();
		$this->counter = $this->level = 0;
		$this->content = '';

		$parser = new \TSax3();
		$parser->set_object($this);
		$parser->set_element_handler('handleTagOpen', 'handleTagClose');
		$parser->set_data_handler('handleTagData');
		$parser->set_escape_handler('handleTagEscape');
		$parser->parse(stripslashes($text));

		$this->replaceNonBreakingSpaces();
	}

	private function replaceNonBreakingSpaces() {
		$this->content = str_replace('&nbsp;', $this->nbspReplace, $this->content);
	}

	private function fillNonBreakingSpaces() {
		$this->content = str_replace($this->nbspReplace, '&nbsp;', $this->content);
	}

	/**
	 * @param array $element
	 */
	private function setCurrentElement($element) {
		$this->elements[$this->level] = $element;
	}

	/**
	 * @return array
	 */
	private function getCurrentElement() {
		return $this->elements[$this->level];
	}

	/**
	 * @param array $attributes
	 * @return string
	 */
	private function pasteAttributes($attributes) {
		$attributesString = '';
		foreach ($attributes as $key => $value) {
			$attributesString .= ' ' . htmlspecialchars($key, ENT_QUOTES) . '="' . htmlspecialchars($value, ENT_QUOTES) . '"';
		}
		return $attributesString;
	}

	public function fillTags() {
		$this->fillNonBreakingSpaces();
		$this->content = str_replace($this->ids, $this->tags, $this->content);
	}

	public function overrideTag($element) {
		$attributes = $element['attributes'];
		$this->tags[array_search($element['id'], $this->ids)] = sprintf($element['pattern'], $element['tag'], $this->pasteAttributes($attributes));
	}

	/**
	 * @param \TSax3 $Parser
	 * @param string $tag
	 * @param array $attributes
	 * @param boolean $isClosed
	 */
	public function handleTagOpen(\TSax3 $Parser, $tag, $attributes, $isClosed = false) {
		$this->counter++;
		$this->level++;

		$pattern = $isClosed ? '<%s%s />' : '<%s%s>';
		$id = $this->tagIDprefix . $this->counter . $this->tagIDsuffix;

		$this->ids[] = $id;
		$this->tags[] = sprintf($pattern, $tag, $this->pasteAttributes($attributes));
		$this->content .= $id;

		$element = array(
			'tag' => $tag,
			'attributes' => $attributes,
			'isClosed' => $isClosed,
			'id' => $id,
			'pattern' => $pattern
		);

		$this->allElements[$id] = $element;

		$this->setCurrentElement($element);
	}

	/**
	 * 
	 * @param \TSax3 $Parser
	 * @param string $tag
	 * @throws \Exception
	 */
	public function handleTagClose(\TSax3 $Parser, $tag) {
		$element = $this->getCurrentElement();
		if ($tag != $element['tag']) {
			print_r($tag);
			print_r($element);
			throw new TagsDoesntMatchException('Tags doesnt match!');
		}
		$closeTag = $element['isClosed'] ? '' : '</' . $element['tag'] . '>';
		$this->content .= $closeTag;
		$this->level--;
	}

	/**
	 * @param \TSax3 $Parser
	 * @param string $data
	 */
	public function handleTagData(\TSax3 $Parser, $data) {
		$this->content .= $data;
	}

	/**
	 * @param \TSax3 $Parser
	 * @param string $data
	 */
	public function handleTagEscape(\TSax3 $Parser, $data) {
		$data = '<!' . $data . '>';
		$this->content .= $data;
	}

	/**
	 * 
	 * @return string
	 */
	public function getContent() {
		return $this->content;
	}

	/**
	 * 
	 * @param string $content
	 */
	public function setContent($content) {
		$this->content = $content;
	}

	public function getTags() {
		return $this->tags;
	}

	public function getAllElements() {
		return $this->allElements;
	}

}
